#include "EpilAssignOperationHelper.h"
#include "EpilIdentifierExpressionCode.h"
#include "EpilFieldExpressionCode.h"
#include "EpilIndexExpressionCode.h"
#include "EpilValueObject.h"
#include "EpilPointerHelper.h"
#include "EpilExpressionInterpreter.h"
#include "EpilInterpreterException.h"


void EpilAssignOperationHelper::Assign( EpilInterpreter * interpreter, const EpilExpressionCodeBase * assignee, EpilObject * assigner )
{
	switch( assignee->GetType() )
	{
	case ECT_IdentifierExpression:
		{
			const EpilIdentifierExpressionCode * identifier = static_cast<const EpilIdentifierExpressionCode *>( assignee );
			interpreter->SetVariable( identifier->GetName(), assigner );
		}
		break;
	case ECT_FieldExpression:
		{
			const EpilFieldExpressionCode * fieldCode = static_cast<const EpilFieldExpressionCode *>( assignee );
			const EpilExpressionCodeBase * tableCode = fieldCode->GetTableExpression();

			EpilExpressionInterpreter ei( interpreter );
			try
			{
				ei.Run( tableCode );
			}
			catch( ... )
			{
				assigner->ReleaseCopy();
				throw;
			}
			EpilObject * obj = ei.GetObject();

			if( obj->GetType() != EOT_Table )
			{
				obj->ReleaseCopy();
				assigner->ReleaseCopy();
				throw EpilInterpreterException( t("Attempt to index a non-table value \'") + tableCode->ToEpilCode() + t("\'"), tableCode->GetLocation() );
			}
			EpilTableObject * table = static_cast<EpilTableObject*>( obj );
			table->SetField( fieldCode->GetFieldName(), assigner );
			table->ReleaseCopy();
		}
		break;
	case ECT_IndexExpression:
		{
			const EpilIndexExpressionCode * indexCode = static_cast<const EpilIndexExpressionCode *>( assignee );
			const EpilExpressionCodeBase * indexExpr = indexCode->GetIndexExpression();
			const EpilExpressionCodeBase * tableExpr = indexCode->GetTableExpression();

			EpilExpressionInterpreter eiIndex( interpreter );
			try
			{
				eiIndex.Run( indexExpr );
			}
			catch( ... )
			{
				assigner->ReleaseCopy();
				throw;
			}
			EpilObject * indexObj = eiIndex.GetObject();

			EpilExpressionInterpreter eiTable( interpreter );
			try
			{
				eiTable.Run( tableExpr );
			}
			catch( ... )
			{
				assigner->ReleaseCopy();
				indexObj->ReleaseCopy();
				throw;
			}
			EpilObject * tableObj = eiTable.GetObject();

			if( tableObj->GetType() != EOT_Table )
			{
				assigner->ReleaseCopy();
				indexObj->ReleaseCopy();
				tableObj->ReleaseCopy();
				throw EpilInterpreterException( t("Attempt to index a non-table value \'") + tableExpr->ToEpilCode() + t("\'"), tableExpr->GetLocation() );
			}
			EpilTableObject * table = static_cast<EpilTableObject*>( tableObj );
			table->SetField( indexObj, assigner );

			indexObj->ReleaseCopy();
			tableObj->ReleaseCopy();
		}
		break;
	}
}


void EpilAssignOperationHelper::AssignLocal( EpilInterpreter * interpreter, const tstring & assignee, EpilObject * assigner )
{
	interpreter->GetCurrentScope()->SetLocal( assignee, assigner );
}